<!--- 
@%HEADER%@


Copyright @%CUR_YEAR%@ Seb Duggan

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--->

<cfcomponent extends="BasePlugin">

	<cffunction name="init" access="public" output="false" returntype="any">
		<cfargument name="mainManager" type="any" required="true" />
		<cfargument name="preferences" type="any" required="true" />	
		
		<cfset setManager(arguments.mainManager) />
		<cfset setPreferencesManager(arguments.preferences) />
		<cfset setPackage("com/sebduggan/mango/plugins/cf8captcha") />
		
		<!--- Default preferences --->
		<cfset initSettings(
				CF8CaptchaDifficulty = "medium",
				CF8CaptchaFonts = "Arial,Verdana,Georgia",
				CF8CaptchaFontSize = "30",
				CF8CaptchaLength = "5"
			) />
		
		<cfset variables.salt = CreateUUID() />
		<cfreturn this/>
	</cffunction>

	<cffunction name="setup" hint="This is run when a plugin is activated" access="public" output="false" returntype="any">
		<cfreturn "CF8 Captcha plugin activated. Would you like to <a href='generic_settings.cfm?event=showCF8CaptchaSettings&amp;owner=cf8captcha&amp;selected=showCF8CaptchaSettings'>configure it now</a>?" />
	</cffunction>
	
	<cffunction name="unsetup" hint="This is run when a plugin is de-activated" access="public" output="false" returntype="any">
		<cfreturn />
	</cffunction>

	<cffunction name="handleEvent" hint="Asynchronous event handling" access="public" output="false" returntype="any">
		<cfargument name="event" type="any" required="true" />
		<cfreturn />
	</cffunction>

	<cffunction name="processEvent" hint="Synchronous event handling" access="public" output="false" returntype="any">
		<cfargument name="event" type="any" required="true" />

			<cfset var data = arguments.event.getData() />
			<cfset var message = arguments.event.getMessage() />
			<cfset var LOCAL = StructNew() />
			<cfset LOCAL.valid = false />
			
			<cfswitch expression="#arguments.event.getName()#">
				<cfcase value="beforeCommentAdd">
					<!--- check for valid captcha, otherwise, reject --->
					<cfif structkeyexists(data.rawdata,"captchaHash") AND structkeyexists(data.rawdata,"captchaText")>
						<cfset LOCAL.hashString = data.rawdata.captchaHash />
						<cfset LOCAL.userInput = data.rawdata.captchaText />
						<cfset LOCAL.valid = not compare(LOCAL.hashString,getHash(LOCAL.userInput)) />
					<cfelseif data.newItem.getAuthorId() NEQ "">
						<!--- maybe this is an author adding a comment from the admin --->
						<cfset LOCAL.valid = true />
					</cfif>
					
					<cfset arguments.event.setContinueProcess(LOCAL.valid) />
					<cfif NOT LOCAL.valid>
						<cfset message.setText("Invalid picture text. Please try again") />
						<cfset message.setStatus("error") />
					</cfif>
				</cfcase>
				<cfcase value="beforeFormToEmailSend">
					<!--- check for valid captcha, otherwise, reject --->
					<cfif structkeyexists(data.originalEventData.externalData,"captchaHash") AND 
							structkeyexists(data.originalEventData.externalData,"captchaText")>
						<cfset LOCAL.hashString = data.originalEventData.externalData.captchaHash />
						<cfset LOCAL.userInput = data.originalEventData.externalData.captchaText />
						<cfset LOCAL.valid = not compare(LOCAL.hashString,getHash(LOCAL.userInput)) />
					</cfif>
					
					<cfset arguments.event.setContinueProcess(LOCAL.valid) />
					<cfif NOT LOCAL.valid>
						<cfset message.text = "Invalid picture text. Please try again" />
						<cfset message.status = "error" />
					</cfif>
				</cfcase>
				
				<cfcase value="beforeCommentFormEnd,beforeFormToEmailEnd">
					<cfset LOCAL.captchaVars = getCaptchaVars() />
					<cfset LOCAL.outputData = arguments.event.getOutputData() />
					<cfset LOCAL.height = Ceiling(getSetting("CF8CaptchaFontSize") * 2.7) />
					<cfset LOCAL.width = Ceiling(getSetting("CF8CaptchaFontSize") * (getSetting("CF8CaptchaLength") + 1) * 1.4) />
					<cfsavecontent variable="LOCAL.formInputs"><cfoutput>
					<p>
						<div style="background-color:white;width:#LOCAL.width+2#px;height:#LOCAL.height+2#px;overflow:hidden;"><cfimage action="captcha" height="#LOCAL.height#" width="#LOCAL.width#" text="#LOCAL.captchaVars.plain#" difficulty="#getSetting("CF8CaptchaDifficulty")#" fontSize="#getSetting("CF8CaptchaFontSize")#" fonts="#getSetting("CF8CaptchaFonts")#" /></div><br />
						<label>Please enter the text you see in the picture:<br />
						<input name="captchaText" type="text" value="" /></label>
						<input name="captchaHash" type="hidden" value="#LOCAL.captchaVars.hashed#" />
					</p>
					</cfoutput></cfsavecontent>
					<cfset arguments.event.setOutputData(LOCAL.outputData & LOCAL.formInputs) />
				</cfcase>
				
				<!--- admin event --->
				<cfcase value="settingsNav">
					<cfset LOCAL.link = structnew() />
					<cfset LOCAL.link.owner = "cf8Captcha">
					<cfset LOCAL.link.page = "settings" />
					<cfset LOCAL.link.title = "CF8 Captcha" />
					<cfset LOCAL.link.eventName = "showCF8CaptchaSettings" />
					
					<cfset arguments.event.addLink(LOCAL.link)>
				</cfcase>
			
				<cfcase value="showCF8CaptchaSettings">		
					<cfif structkeyexists(data.externaldata,"apply")>
					
						<cfset setSettings(
								CF8CaptchaDifficulty = data.externaldata.CF8CaptchaDifficulty,
								CF8CaptchaFonts = data.externaldata.CF8CaptchaFonts,
								CF8CaptchaFontsize = data.externaldata.CF8CaptchaFontsize,
								CF8CaptchaLength = data.externaldata.CF8CaptchaLength
							) />
						
						<!--- Save the new values to the settings file --->
						<cfset persistSettings() />
						
						<cfset data.message.setstatus("success") />
						<cfset data.message.setType("settings") />
						<cfset data.message.settext("Settings updated")/>
					</cfif>
					
					<cfsavecontent variable="LOCAL.page">
						<cfinclude template="admin/settingsForm.cfm">
					</cfsavecontent>
						
						<!--- change message --->
						<cfset data.message.setTitle("CF8 Captcha settings") />
						<cfset data.message.setData(LOCAL.page) />
				</cfcase>
				
			</cfswitch>
		
		<cfreturn arguments.event />
	</cffunction>

	<cffunction name="getCaptchaVars" output="no" returntype="struct">
		
		<cfset var LOCAL = StructNew() />
		<cfset LOCAL.length = RandRange(Max(getSetting("CF8CaptchaLength") - 1,1), getSetting("CF8CaptchaLength") + 1) />
		
		<cfset LOCAL.source = "23456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ" />
		<cfset LOCAL.result = StructNew() />
		<cfset LOCAL.result.plain = "" />
		
		<cfloop from="1" to="#LOCAL.length#" index="LOCAL.i">
			<cfset LOCAL.rand = RandRange(1, Len(LOCAL.source)) />
			<cfset LOCAL.result.plain = LOCAL.result.plain & Mid(LOCAL.source, LOCAL.rand, 1) />
		</cfloop>
		
		<cfset LOCAL.result.hashed = getHash(LOCAL.result.plain) />
		
		<cfreturn LOCAL.result />
	</cffunction>
	
	<cffunction name="getHash" output="no" returntype="string">
		<cfargument name="textToHash" type="string" required="true" />
		
		<cfreturn Hash(variables.salt & LCase(arguments.textToHash),"SHA") />
	</cffunction>

</cfcomponent>